package com.yang.frost.blueberry.config;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.yang.frost.blueberry.config.properties.Swagger2Properties;
import io.swagger.annotations.Api;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.RequestHandler;
import springfox.documentation.annotations.ApiIgnore;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.ApiSelectorBuilder;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author 10296
 * @date 2021/1/13
 * @Description: Swagger2全局配置
 */
@Configuration
@EnableSwagger2
public class Swagger2Config {

    private Logger logger = LoggerFactory.getLogger(Swagger2Config.class);

    @Autowired
    private Swagger2Properties swagger2Properties;
    /**
     * 扫描多包时，包路径的拆分符,分号
     */
    private static final String SPLIT_COMMA = ",";

    /**
     * 扫描多包时，包路径的拆分符,逗号
     */
    private static final String SPLIT_SEMICOLON = ";";


    /**
     * Swagger2 忽略得参数类型
     */
    private Class<?>[] ignoredParameterTypes = new Class[]{
            ServletRequest.class,
            ServletResponse.class,
            HttpServletRequest.class,
            HttpServletResponse.class,
            HttpSession.class,
            ApiIgnore.class
    };

    @Bean
    public Docket createRestApi() {

        //获取需要扫描得包
        String[] basePackages = getBasePackages();
        ApiSelectorBuilder apiSelectorBuilder = new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select();
        //如果扫描得包为空，则默认扫描类上有@Api注解的类
        if (ArrayUtils.isEmpty(basePackages)) {
            apiSelectorBuilder.apis(RequestHandlerSelectors.withClassAnnotation(Api.class));

        } else {
            //扫描指定的包
            apiSelectorBuilder.apis(basePackage(basePackages));
        }


        Docket docket = apiSelectorBuilder.paths(PathSelectors.any())
                .build()
                .enable(swagger2Properties.isEnable())
                .ignoredParameterTypes(ignoredParameterTypes)
                .globalOperationParameters(getParameters());


        return docket;

    }

    private List<Parameter> getParameters() {
        // 获取自定义参数配置
        List<Swagger2Properties.ParameterConfig> parameterConfig = swagger2Properties.getParameterConfig();
        if (CollectionUtils.isEmpty(parameterConfig)) {
            return null;
        }
        List<Parameter> parameters = new ArrayList<>();
        parameterConfig.forEach(parameter -> {
            // 设置自定义参数
            parameters.add(new ParameterBuilder()
                    .name(parameter.getName())
                    .description(parameter.getDescription())
                    .modelRef(new ModelRef(parameter.getDataType()))
                    .parameterType(parameter.getType())
                    .required(parameter.isRequired())
                    .defaultValue(parameter.getDefaultValue())
                    .build());
        });
        return parameters;

    }

    /**
     * 添加额外参数
     *
     * @param basePackages
     * @return
     */
    private Predicate<RequestHandler> basePackage(String[] basePackages) {

        return input -> declaringClass(input).transform(handlerPackage(basePackages)).or(true);

    }

    /**
     * 获取ApiInfo
     *
     * @return
     */
    private ApiInfo apiInfo() {

        return new ApiInfoBuilder()
                .title(swagger2Properties.getTitle())
                .description(swagger2Properties.getDescription())
                .termsOfServiceUrl(swagger2Properties.getUrl())
                .contact(new Contact(swagger2Properties.getContactName(), swagger2Properties.getContactUrl(), swagger2Properties.getContactEmail()))
                .version(swagger2Properties.getVersion())
                .build();

    }

    /**
     * 获取扫描得包
     *
     * @return
     */
    private String[] getBasePackages() {

        String basePackage = swagger2Properties.getBasePackage();
        String[] basePackages = null;
        try {
            if (StringUtils.isBlank(basePackage)) {
                throw new Exception("Swagger 包不能为空!");
            }
            if (basePackage.contains(SPLIT_COMMA)) {
                basePackages = basePackage.split(SPLIT_COMMA);
            } else if (basePackage.contains(SPLIT_SEMICOLON)) {
                basePackages = basePackage.split(SPLIT_SEMICOLON);
            }


        } catch (Exception e) {
            e.printStackTrace();
        }

        logger.info("Swagger2  scan packages: 【" + Arrays.toString(basePackages) + "】");
        return basePackages;

    }

    private static Optional<? extends Class<?>> declaringClass(RequestHandler input) {
        return Optional.fromNullable(input.declaringClass());
    }

    private static Function<Class<?>, Boolean> handlerPackage(final String[] basePackages) {
        return input -> {
            // 循环判断匹配
            for (String strPackage : basePackages) {
                boolean isMatch = input.getPackage().getName().startsWith(strPackage);
                if (isMatch) {
                    return true;
                }
            }
            return false;
        };
    }

}
